package com.inetools.gwt.gajaxs.client;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.ObjectElement;
import com.google.gwt.dom.client.ParamElement;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.URL;
import com.google.gwt.logging.client.LogConfiguration;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.inetools.gwetons.client.RegExpJsni;
import com.inetools.gwetons.client.RegExpJsniIntf;
import com.inetools.gwt.gajaxs.client.CallbackeeAbstractImpl.PostSendingRequestListener;
import com.inetools.gwt.gajaxs.client.GDataFeedModel.Link;

public class PicasaAlbumsProfiler extends Composite {
	public static final int DEFAULT_CALLBACK_MAX_COUNT = 2;
	public static final int DEFAULT_CALLBACK_REQUEST_TIME_OUT = 5000;
	public static final int MIN_CALLBACK_REQUEST_TIME_OUT = 1000;
	public static final int DEFAULT_ALBUM_NUM_PER_ROW = 2;
	public static final boolean DEFAULT_ALBUM_LOCATION_SWITCH = false;
	public static final boolean DEFAULT_ALBUM_NAME_SWITCH = false;
	public static final boolean DEFAULT_ALBUM_PHOTO_COUNTS_SWITCH = true;
	public static final boolean DEFAULT_ALBUM_THUMBNAIL_SWITCH = true;
	public static final boolean DEFAULT_ALBUM_TITLE_SWITCH = true;
	public static final boolean DEFAULT_ALBUM_SUMMARY_SWITCH = true;
	public static final boolean DEFAULT_ALBUM_UPDATED_TIME_SWITCH = false;
	public static final boolean DEFAULT_ALBUM_USER_NAME_SWITCH = false;
	// 94, 110, 128, 200, 220, 288, 320, 400, 512, 576, 640, 720, 
	// 800, 912, 1024, 1152, 1280, 1440, 1600
//	public static final int DEFAULT_EMBEDED_SLIDESHOW_WIDTH = 640;
//	public static final int MIN_EMBEDED_SLIDESHOW_WIDTH = 200;
	
	public interface CallbackListener {
		public void dispatchFailureNotice( FlexTable flextable);
		public void dispatchPreCallback( GPhotoAlbumFeedData gPhotoAlbumFeedData, FlexTable flextable);
		public void dispatchPostCallback( GPhotoAlbumFeedData gPhotoAlbumFeedData, FlexTable flextable);
	}
	
	protected class PicasaAlbumsProfilerServiceImpl extends CallbackeeAbstractImpl {
		// Provide methods by Callbackee interface ----------------------------------------------------
		protected String picasaUrl;
		@Override
		public String getRequestUrlForCallback() {
			return picasaUrl;
		}
		
		protected LinkedHashSet<CallbackListener> callbackListenerLinkedHashSet = 
			new LinkedHashSet<CallbackListener>();
		public boolean addCallbackListener( CallbackListener callbackListener) {
			if ( callbackListener == null) return false;
			return callbackListenerLinkedHashSet.add( callbackListener);
		}
		public boolean removeCallbackListener( CallbackListener callbackListener) {
			return callbackListenerLinkedHashSet.remove( callbackListener);
		}
		public void clearCallbackListeners() {
			callbackListenerLinkedHashSet = new LinkedHashSet<CallbackListener>();
		}
		
		@Override
		public void fireCallbackFailureEvent() {
			String errorMessage = "Callback didn't occure before time-out period.";
			GWT.log( errorMessage);
/*			
			Label label = new Label( errorMessage);
			flexTable.setWidget( 0, 0, label);
*/
			for( CallbackListener callbackListener : callbackListenerLinkedHashSet) {
				callbackListener.dispatchFailureNotice( flexTable);
			} // for
			
		}
		
		protected class PopupHandler implements ClickHandler {
			Logger logger = Logger.getLogger( this.getClass().toString());
				void log( Level level, String message) {
					if ( LogConfiguration.loggingIsEnabled()) {
//Remove the following if-blocks of println statements once figure out how to make logger work in GWT's dev mode
if ( !GWT.isScript()) {
	if ( level.intValue() > Level.INFO.intValue()) {
		System.err.println( "[" + level.getName() + "]" + message);
	}
	else {
		System.out.println( "[" + level.getName() + "]" + message);
	}
}
						logger.log( level, message);
					}
				}
			
			protected String picasaSlideshowUrl 
			= "http://picasaweb.google.com/s/c/bin/slideshow.swf";
			protected String picasaSlideshowUrlQueryParameterStr 
			= "host=picasaweb.google.com&captions=1&feat=flashalbum&RGB=0x000000&feed=";
			
			protected String title;
			protected String summary;
			protected PicasaAlbumSlideshowPopupDialogService picasaAlbumSlideshowPopupDialogService;
			
			protected final RegExpJsniIntf feedLinkRelRegExpObj 
			= new RegExpJsni( "#feed$", true, false, false);
			protected final RegExpJsniIntf albumIdUrlRegExpObj 
			= new RegExpJsni( "^https?\\:\\/\\/photos\\.googleapis\\.com\\/", true, false, false);
			
			public PopupHandler( 
					GPhotoAlbumFeedData.Entry entry, 
					PicasaAlbumSlideshowPopupDialogService picasaAlbumSlideshowPopupDialogService) 
			throws IllegalArgumentException {
				log( Level.FINER, "Entered " + PopupHandler.class.getName() + "constructor for " + this.toString());
				
				title = entry.getTitle();
				summary = entry.getSummary();
				
				String picasaSlideshowUrlSuffix = null;
				JsArray<Link> linkJsArray = entry.getLinks();
				for( int i = 0; i < linkJsArray.length(); i++) {
					Link link = linkJsArray.get( i);
						log( 
								Level.FINEST, 
								"Evaluating next link as suitable slideshow URL:" 
								+ link.getHref().toString());
						
					if ( feedLinkRelRegExpObj.test( link.getRel())) {
						if ( albumIdUrlRegExpObj.test( link.getHref())) {
							picasaSlideshowUrlSuffix = albumIdUrlRegExpObj.getRightContext();
							int altIndex = picasaSlideshowUrlSuffix.indexOf( "alt=json-in-script");
							if ( altIndex > -1) {
								picasaSlideshowUrlSuffix = 
									"http://picasaweb.google.com/" + 
									picasaSlideshowUrlSuffix.substring( 0, altIndex) + 
									"alt=rss" + 
									picasaSlideshowUrlSuffix.substring( altIndex + "alt=json-in-script".length());
								picasaSlideshowUrlSuffix = URL.encodeQueryString( picasaSlideshowUrlSuffix);
							}
						}
					}
				} // for
				
				if ( picasaSlideshowUrlSuffix == null) {
					new IllegalArgumentException( 
							"Could not find the appropriate link of which the rel value means for feed in the " + entry.getGPhotoName() + " entry.");
				}
				picasaSlideshowUrlQueryParameterStr 
				= picasaSlideshowUrlQueryParameterStr + picasaSlideshowUrlSuffix;
					log( 
							Level.FINEST, 
							"Constructed slideshow URL: " + picasaSlideshowUrlQueryParameterStr);
					
				this.picasaAlbumSlideshowPopupDialogService = picasaAlbumSlideshowPopupDialogService;
				
				log( Level.FINER, "Exited " + PopupHandler.class.getName() + "constructor for " + this.toString());
			} // public PopupHandler

			@Override
			public void onClick( ClickEvent event) {
				log( Level.FINER, "Entered onClick method of " + this.toString());
				
				DialogBox dialogBox = picasaAlbumSlideshowPopupDialogService.getDialogBoxCell();
				dialogBox.setText( title);
				picasaAlbumSlideshowPopupDialogService.getCaptionLabelCell().setText( summary);
				
				HTMLPanel slideshowObjectWrapperHTMLPanel = 
					picasaAlbumSlideshowPopupDialogService.getSlideshowObjectWrapperHTMLPanel();
				NodeList<Node> objectWrapperChildNodeList = 
					slideshowObjectWrapperHTMLPanel.getElement().getChildNodes();
				ObjectElement slideshowObjectElement = null;
				for( int i = 0; i < objectWrapperChildNodeList.getLength(); i++) {
					String nodeName = objectWrapperChildNodeList.getItem( i).getNodeName();
					if ( nodeName.equalsIgnoreCase( "object")) {
						slideshowObjectElement =
							ObjectElement.as( Element.as( objectWrapperChildNodeList.getItem( i)));
						break;
					}
				} // for
				if ( slideshowObjectElement != null) {
					NodeList<Node> slideshowObjectChildeNodeList = slideshowObjectElement.getChildNodes();
					ParamElement movieParamElement = null;
					for( int i = 0; i < slideshowObjectChildeNodeList.getLength(); i++) {
						String nodeName = slideshowObjectChildeNodeList.getItem( i).getNodeName();
						if ( nodeName.equalsIgnoreCase( "embed")) {
							Element embedElement = Element.as( slideshowObjectChildeNodeList.getItem( i));
							embedElement.setAttribute( "src", picasaSlideshowUrl);
							embedElement.setAttribute( "flashvars", picasaSlideshowUrlQueryParameterStr);
						}
						else if ( nodeName.equalsIgnoreCase( "param")) {
							movieParamElement = 
								ParamElement.as( Element.as( slideshowObjectChildeNodeList.getItem( i)));
							if ( !movieParamElement.getName().equalsIgnoreCase( "movie")) {
								movieParamElement = null;
							}
						}
					} // for
					if ( movieParamElement != null) {
						slideshowObjectElement.removeChild( movieParamElement);
					}
					movieParamElement = Document.get().createParamElement();
					movieParamElement.setName( "movie");
					movieParamElement.setValue( 
							picasaSlideshowUrl + "?" + picasaSlideshowUrlQueryParameterStr);
					slideshowObjectElement.insertFirst( movieParamElement);
					
					Node newObjectNode = slideshowObjectElement.cloneNode( true);
					slideshowObjectWrapperHTMLPanel.getElement().replaceChild( 
							newObjectNode, slideshowObjectElement);
						log( 
								Level.FINEST, 
								"Replaced object element for picasa slideshow to " 
								+ newObjectNode.toString()
								);
				}
				
				dialogBox.center();
				
				log( Level.FINER, "Exited onClick method of " + this.toString());
			} // public void onClick( ClickEvent event)
		} // protected class PopupHandler implements ClickHandler
		
		protected List<String> excludingIdList = null;
		
		@Override
		public void fireCallbackEvent( JavaScriptObject jsObj) {
			GPhotoAlbumFeedData gPhotoAlbumFeedData = (GPhotoAlbumFeedData)jsObj;

			for( CallbackListener callbackListener : callbackListenerLinkedHashSet) {
				callbackListener.dispatchPreCallback( gPhotoAlbumFeedData, flexTable);
			} // for
			
			int flexTableCellIndex = flexTable.getRowCount();
			if ( flexTableCellIndex > 0) {
				flexTableCellIndex = flexTable.getRowCount() - 1;
				flexTableCellIndex = 
					flexTableCellIndex * albumNumPerRow + flexTable.getCellCount( flexTableCellIndex);
			}
			else {
				flexTableCellIndex = -1;
			}
			
			
			if ( gPhotoAlbumFeedData.getEntries().length() > 0) {
				for( int i = 0; i < gPhotoAlbumFeedData.getEntries().length(); i++) {
					GPhotoAlbumFeedData.Entry entry = gPhotoAlbumFeedData.getEntries().get( i);
					
					if ( excludingIdList != null) {
						if ( excludingIdList.contains( entry.getGPhotoId())) {
							continue; // for
						}
					}
					
					PopupHandler popupHandler = 
						new PopupHandler( entry, picasaAlbumSlideshowPopupDialogService);
					
					if ( picasaAlbumProfilerInputData.isAlbumLocationOn()) {
						picasaAlbumUnitFaceService.getLocationCell().setText( 
								"Location: " + entry.getGPhotoLocation());
					}
					if ( picasaAlbumProfilerInputData.isAlbumNameOn()) {
						Label nameCellLabel = picasaAlbumUnitFaceService.getNameCell();
						nameCellLabel.setText( entry.getGPhotoName());
//						nameCellLabel.addClickHandler( popupHandler);
					}
					if ( picasaAlbumProfilerInputData.isAlbumTitleOn()) {
						Label titleCellLabel = picasaAlbumUnitFaceService.getTitleCell();
						titleCellLabel.setText( entry.getTitle());
//						titleCellLabel.addClickHandler( popupHandler);
					}
					if ( picasaAlbumProfilerInputData.isAlbumSummaryOn()) {
						picasaAlbumUnitFaceService.getSummaryCell().setText( entry.getSummary());
					}
					if ( picasaAlbumProfilerInputData.isAlbumPhotoCountsOn()) {
						picasaAlbumUnitFaceService.getPhotoCountsCell().setText( 
								entry.getGPhotoNumPhotos() + " photos");
					}
					if ( picasaAlbumProfilerInputData.isAlbumThumbnailOn()) {
						Image thumbnailCellImage = picasaAlbumUnitFaceService.getThumbnailCell();
						SafeUri thumbnailSafeUri 
						= UriUtils.fromString( entry.getMediaGroup().getThumbnails().get( 0).getUrl());
						thumbnailCellImage.setUrl( thumbnailSafeUri);
//						thumbnailCellImage.addClickHandler( popupHandler);
					}
					if ( picasaAlbumProfilerInputData.isAlbumUpdatedTimeOn()) {
						picasaAlbumUnitFaceService.getUpdatedTimeCell().setText( 
								"Last update:" + entry.getUpdated());
					}
					if ( picasaAlbumProfilerInputData.isAlbumUserNameOn()) {
						picasaAlbumUnitFaceService.getUserNameCell().setText( 
								"By " + entry.getGPhotoUser());
					}
					
					Element albumUnitFaceElement = picasaAlbumUnitFaceService.getElement();
					Node albumUnitFaceNode = albumUnitFaceElement.cloneNode( true);

					HTML html = new HTML();
					html.getElement().appendChild( albumUnitFaceNode);
					html.addClickHandler( popupHandler);
					html.getElement().getStyle().setCursor( Cursor.POINTER);
					
					++flexTableCellIndex;
					flexTable.setWidget( flexTableCellIndex / albumNumPerRow, flexTableCellIndex % albumNumPerRow, html);
				} // for
			}
			
			for( CallbackListener callbackListener : callbackListenerLinkedHashSet) {
				callbackListener.dispatchPostCallback( gPhotoAlbumFeedData, flexTable);
			} // for
			
		} // public void fireCallbackEvent( JavaScriptObject jsObj)

		protected String callbackBaseName;
		{
			callbackBaseName = this.getClass().getName();
			callbackBaseName = callbackBaseName.substring( callbackBaseName.lastIndexOf( '.') + 1);
		}
		@Override
		public String getCallbackBaseName() {
			return callbackBaseName;
		}

		@Override
		public int getCallbackId() {
			return this.hashCode();
		}

		protected int callbackMaxCount = DEFAULT_CALLBACK_MAX_COUNT;
		@Override
		public int getCallbackMaxCount() {
			return callbackMaxCount;
		}

		protected int callbackRequestTimeOut = DEFAULT_CALLBACK_REQUEST_TIME_OUT;
		@Override
		public int getCallbackRequestTimeOut() {
			return callbackRequestTimeOut;
		}

		String callbackTimeStamp = null;
		@Override
		public String getCallbackTimeStamp() {
			return callbackTimeStamp;
		}
		@Override
		public void setCallbackTimeStamp(String timeStamp) {
			callbackTimeStamp = timeStamp;
		}

		protected int jsTimeoutObjReferrer = -1;
		@Override
		public void setJsTimeoutObjReferrerForCallback(int jsTimeoutObjReferrer) {
			this.jsTimeoutObjReferrer = jsTimeoutObjReferrer;
		}
		// --------------------------------------------------------------------------------------------

		public void initiate() {
			sendRequest( this);
		}		
		
		protected PicasaAlbumProfilerInputService picasaAlbumProfilerInputData;
		protected int albumNumPerRow = DEFAULT_ALBUM_NUM_PER_ROW;
		protected PicasaAlbumUnitFaceService picasaAlbumUnitFaceService;
		protected PicasaAlbumSlideshowPopupDialogService picasaAlbumSlideshowPopupDialogService;
		protected FlexTable flexTable;
		
		public PicasaAlbumsProfilerServiceImpl( 
				PicasaAlbumProfilerInputService picasaAlbumProfilerInputData,
				PicasaAlbumUnitFaceService picasaAlbumUnitFaceService, 
				PicasaAlbumSlideshowPopupDialogService picasaAlbumSlideshowPopupDialogService, 
				FlexTable flexTable) 
		throws IllegalArgumentException {
			if ( picasaAlbumProfilerInputData == null) {
				throw new IllegalArgumentException( 
						"picasaAlbumProfilerInputData should never be null as the constructor argument.");
			}
			this.picasaAlbumProfilerInputData = picasaAlbumProfilerInputData;
			picasaUrl = picasaAlbumProfilerInputData.getPicasaUrl();
			if ( picasaUrl == null) {
				throw new IllegalArgumentException( "picasaUrl should never be null.");
			}
			if ( "".equals( picasaUrl)) {
				throw new IllegalArgumentException( "picasaUrl should never be empty string.");
			}
			RegExpJsniIntf regExpJsniObj 
			= new RegExpJsni( 
					"^https?\\:\\/\\/photos\\.googleapis\\.com\\/data\\/feed\\/api\\/user\\/[^\\/\\s]+\\?", 
					false, false, false);
			if ( !regExpJsniObj.test( picasaUrl)) {
				throw new IllegalArgumentException(
						"\"" + picasaUrl + "\" is invalid for Google photo data feed with callback.");
			}
			String picasaUrlParameter = regExpJsniObj.getRightContext();
			JavaScriptObject regExpObj = 
				RegExpJsni.getRegExp( "([^\\s\\&\\=]+\\=[^\\s\\&\\=]+\\&)*kind=album\\&", false, false);
			if ( !RegExpJsni.test( regExpObj, picasaUrlParameter)) {
				throw new IllegalArgumentException( 
						"\"" + picasaUrl + "\" does not contain the kind parameter set to the proper value set: kind=album");
			}
			regExpObj = 
				RegExpJsni.getRegExp( "([^\\s\\&\\=]+\\=[^\\s\\&\\=]+\\&)*alt=json-in-script\\&", false, false);
			if ( !RegExpJsni.test( regExpObj, picasaUrlParameter)) {
				throw new IllegalArgumentException( 
						"\"" + picasaUrl + "\" does not contain the alt parameter set to the proper value set: alt=json-in-script");
			}
			regExpObj = 
				RegExpJsni.getRegExp( 
					"&callback=$", false, false);
			if ( !RegExpJsni.test( regExpObj, picasaUrlParameter)) {
				throw new IllegalArgumentException( 
						"\"" + picasaUrl + "\" does not end with (incomplete) callback parameter");
			}
			
			
			if ( picasaAlbumProfilerInputData.getCallbackMaxCount() > 0) 
				callbackMaxCount = picasaAlbumProfilerInputData.getCallbackMaxCount();
			if ( picasaAlbumProfilerInputData.getCallbackRequestTimeOut() > MIN_CALLBACK_REQUEST_TIME_OUT) 
				callbackRequestTimeOut = picasaAlbumProfilerInputData.getCallbackRequestTimeOut();
			if ( picasaAlbumProfilerInputData.getAlbumNumPerRow() > 0) 
				albumNumPerRow = picasaAlbumProfilerInputData.getAlbumNumPerRow();

			excludingIdList = Arrays.asList( picasaAlbumProfilerInputData.getExclusionIds());
			
			if ( picasaAlbumUnitFaceService == null) {
				throw new IllegalArgumentException( 
						"picasaAlbumUnitFaceService should never be null as the constructor argument.");
			}
			this.picasaAlbumUnitFaceService = picasaAlbumUnitFaceService;
			
			if ( picasaAlbumSlideshowPopupDialogService == null) {
				throw new IllegalArgumentException( 
					"picasaAlbumSlideshowPopupDialogService should never be null as the constructor argument.");
			}
			DialogBox dialogBox = picasaAlbumSlideshowPopupDialogService.getDialogBoxCell();
			dialogBox.setAnimationEnabled( true);
			dialogBox.setGlassEnabled( true);
			dialogBox.setModal( true);
			this.picasaAlbumSlideshowPopupDialogService = picasaAlbumSlideshowPopupDialogService;
			
			if ( flexTable == null) {
				throw new IllegalArgumentException( 
						"flexTable should never be null as the constructor argument.");
			}
			this.flexTable = flexTable;
		}
	} // protected class PicasaAlbumsProfilerServiceImpl extends CallbackeeAbstractImpl

	protected PicasaAlbumsProfilerServiceImpl picasaAlbumsProfilerServiceImpl;
	
//TODO change to table supporting pagenation. Refer to incubator: http://code.google.com/docreader/#p=google-web-toolkit-incubator&s=google-web-toolkit-incubator&t=Tables	
	protected FlexTable flexTable = new FlexTable();
	public FlexTable getFlexTable() {
		return flexTable;
	}

	public PicasaAlbumsProfiler( 
			PicasaAlbumProfilerInputService picasaAlbumProfilerInputData, 
			PicasaAlbumUnitFaceService picasaAlbumUnitFaceService, 
			PicasaAlbumSlideshowPopupDialogService picasaAlbumSlideshowPopupDialogService) 
	throws IllegalArgumentException {
		
		picasaAlbumsProfilerServiceImpl = 
			new PicasaAlbumsProfilerServiceImpl( 
					picasaAlbumProfilerInputData, 
					picasaAlbumUnitFaceService, 
					picasaAlbumSlideshowPopupDialogService, 
					flexTable);
		
		// All composites must call initWidget() in their constructors.
		initWidget( flexTable);
	}
	
	public void showAlbums() {
		picasaAlbumsProfilerServiceImpl.initiate();
	}
	public boolean addPostSendingRequestListener( PostSendingRequestListener listener) {
		return picasaAlbumsProfilerServiceImpl.addPostSendingRequestListener( listener);
	}
	public boolean removePostSendingRequestListener( PostSendingRequestListener listener) {
		return picasaAlbumsProfilerServiceImpl.removePostSendingRequestListener( listener);
	}
	public void clearPostSendingRequestListeners() {
		picasaAlbumsProfilerServiceImpl.clearPostSendingRequestListeners();
	}
	
	public boolean addCallbackListener( CallbackListener callbackListener) {
		return picasaAlbumsProfilerServiceImpl.addCallbackListener( callbackListener);
	}
	public boolean removeCallbackListener( CallbackListener callbackListener) {
		return picasaAlbumsProfilerServiceImpl.removeCallbackListener( callbackListener);
	}
	public void clearCallbackListeners() {
		picasaAlbumsProfilerServiceImpl.clearCallbackListeners();
	}
	
}
